/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.web.service.support.storage.mgt;

import com.sshtools.j2ssh.SftpClient;
import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.authentication.AuthenticationProtocolState;
import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;
import com.sshtools.j2ssh.transport.HostKeyVerification;
import com.sshtools.j2ssh.transport.TransportProtocolException;
import com.sshtools.j2ssh.transport.publickey.SshPublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vacoor.nothing.common.util.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

/**
 * TODO 增加缓存到本地
 *
 * @author vacoor
 */
public class SftpStorageManagerImpl extends GeneralStorageManager {
    private static final Logger LOG = LoggerFactory.getLogger(SftpStorageManagerImpl.class);

//    private static final String TEMP_FILE_PREFIX = SftpStorageImpl.class.getName() + "-";
//    private static final String TEMP_FILE_SUFFIX = ".tmp";

    public static final int DEFAULT_PORT = 22;

    private final ThreadLocal<SshClient> sshClient = new ThreadLocal<SshClient>();  // TODO 读写后添加是否关闭连接

    private String host;
    private int port = DEFAULT_PORT;
    private String username;
    private String password;

    @Override
    public boolean exists(String remotePath) {
        try {
            SftpClient sftp = getSftpClient();
            List<?> list = sftp.ls(remotePath); // 不存在 IOException
            return 0 < list.size();
        } catch (IOException ignore) {
        }
        return false;
    }

    @Override
    public boolean readTo(String remotePath, OutputStream out, boolean closeOut) throws IOException {
        boolean success = false;
        try {
            SftpClient sftp = getSftpClient();
            sftp.get(remotePath, out);

            out.flush();
            if (closeOut) {
                IOUtils.close(out);
            }
            success = true;
        } catch (IOException e) {
            LOG.warn("read file failed", e);
            throw e;
        } finally {
            disconnect();
        }

        return success;
    }

    @Override
    public void store(InputStream is, String remotePath) throws IOException {
        remotePath = normalizePath(remotePath);

        int i = remotePath.lastIndexOf("/");
        SftpClient sftp = getSftpClient();

        sftp.mkdirs(0 < i ? remotePath.substring(0, i) : remotePath);
        sftp.put(is, remotePath);
    }

    @Override
    public void mkdirs(String path) {

    }

    @Override
    public void close() {
        disconnect();
    }

    /* ****************************************
     *         SFTP Helper Method
     * ****************************************/

    /**
     * 获取当前线程上下文中可用的 sftp client
     *
     * @return
     * @throws IOException
     */
    protected SftpClient getSftpClient() throws IOException {
        SshClient ssh = getSshClient();
        SftpClient sftp = ssh.hasActiveSftpClient() ? ssh.getActiveSftpClient() : null;

        if (null != sftp && !sftp.isClosed()) {
            return sftp;
        }

        return ssh.openSftpClient();
    }

    /**
     * 获取当前线程上下文中可用的 ssh client
     *
     * @return
     * @throws IOException
     */
    protected SshClient getSshClient() throws IOException {
        SshClient ssh = sshClient.get();
        if (null != ssh && ssh.isConnected() && ssh.isAuthenticated()) {
            return ssh;
        }

        ssh = new SshClient();
        ssh.connect(getHost(), getPort(), new YesHostKeyVerificationImpl());

        // PublicKeyAuthenticationClient pkc = new PublicKeyAuthenticationClient();
        // pkc.setKey(new SshRsaPrivateKey());

        PasswordAuthenticationClient pac = new PasswordAuthenticationClient();
        pac.setUsername(getUsername());
        pac.setPassword(getPassword());

        int state = ssh.authenticate(pac);
        if (AuthenticationProtocolState.COMPLETE != state) {
            LOG.debug("authentication failed");
            throw new IOException("authentication failed, state:" + state);
        }

        LOG.debug("connect...");

        sshClient.set(ssh);
        return ssh;
    }

    protected void disconnect() {
        try {
            SshClient ssh = sshClient.get();
            if (null == ssh || !ssh.isConnected()) {
                return;
            }

            SftpClient sftp = ssh.getActiveSftpClient();
            if (null != sftp && !sftp.isClosed()) {
                sftp.quit();
            }
            ssh.disconnect();
        } catch (IOException ignore) {
            // ignore
        } finally {
            sshClient.set(null);
        }
    }

    /* *************************
     *     GETTER / SETTER
     * *************************/

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    /* ********************
     *
     * ****************** */

    private static class YesHostKeyVerificationImpl implements HostKeyVerification {

        @Override
        public boolean verifyHost(String s, SshPublicKey sshPublicKey) throws TransportProtocolException {
            return true;
        }
    }
}
